package top.xzxsrq.fileviewutils.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import top.xzxsrq.common.utils.FileUtilsZX;
import top.xzxsrq.common.utils.StringUtilsZX;
import top.xzxsrq.fileviewutils.FileViewUtilsApplication;
import top.xzxsrq.fileviewutils.entity.*;
import top.xzxsrq.fileviewutils.exception.BusinessException;
import top.xzxsrq.fileviewutils.fileFilter.DirFilter;
import top.xzxsrq.fileviewutils.fileFilter.FileFilter;
import top.xzxsrq.fileviewutils.mapper.DownloadFileMapper;
import top.xzxsrq.fileviewutils.mapper.ShareFileMapper;
import top.xzxsrq.fileviewutils.mapper.SysUserMapper;
import top.xzxsrq.fileviewutils.service.FileService;
import top.xzxsrq.fileviewutils.service.UserDetailsServiceImpl;
import top.xzxsrq.web.utils.AjaxResult;
import top.xzxsrq.utils.PathUtils;
import top.xzxsrq.web.utils.ResultCode;
import top.xzxsrq.web.utils.WebUtils;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class FileServiceImpl implements FileService {
    public static String baseFilePath;

    @Resource
    private Environment environment;

    @PostConstruct
    public void init() {
        String property = environment.getProperty("adminUser.baseFilePath");
        if (property == null) {
            property = "./fileBrowserData";
        }
        baseFilePath = PathUtils.pathResolve(FileViewUtilsApplication.projectDir, property);
        FileUtilsZX.createDir(baseFilePath);
    }

    @Autowired
    private SysUserMapper sysUserMapper;

    public String getBaseFilePathByUserId() {
        SysUserWithRole user = (SysUserWithRole) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        return getBaseFilePathByUserId(user.getUserId());
    }

    public String getBaseFilePathByUserId(String userId) {
        if (UserDetailsServiceImpl.INIT_ADMIN_ID.equals(userId)) {
            return baseFilePath;
        }
        SysUserWithRole user = (SysUserWithRole) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        String path = "";
        if (userId.equals(user.getUserId())) {
            path = user.getUserpath();
        } else {
            SysUser sysUser = sysUserMapper.selectById(userId);
            path = sysUser.getUserpath();
        }
        return getBaseFilePathByUserPath(path);
    }

    private String getBaseFilePathByUserPath(String path) {
        String s = PathUtils.pathResolve(baseFilePath, path);
        boolean childPath = PathUtils.isChildPath(s, baseFilePath);
        if (!childPath) {
            throw new BusinessException("路径错误");
        }
        File file = new File(s);
        if (!file.exists()) {
            file.mkdirs();
        }
        return s;
    }

    /**
     * @param basePath
     * @param dir      是否只过滤文件夹
     * @return
     */
    private AjaxResult getFileList(String basePath, boolean dir) {
        String s = PathUtils.pathResolve(getBaseFilePathByUserId(), basePath);
        boolean childPath = PathUtils.isChildPath(s, getBaseFilePathByUserId());
        if (!childPath) {
            return AjaxResult.failure("传入basePath路径有错" + basePath);
        }
        File file = new File(s);
        if(!file.exists()) {
            return AjaxResult.success(new ArrayList<>(0));
        }
        File[] files = file.listFiles();
        if(files == null) {
            return AjaxResult.success(new ArrayList<>(0));
        }
        List<FileResult> collect = Arrays.stream(files).map(i -> {
                    FileResult fileResult = new FileResult();
                    fileResult.setDirectory(i.isDirectory());
                    fileResult.setFileName(i.getName());
                    return fileResult;
                })
                .filter(i -> {
                    if (dir) {
                        return i.isDirectory();
                    } else {
                        return true;
                    }
                })
                .collect(Collectors.toList());
        return AjaxResult.success(collect);
    }

    @Override
    public AjaxResult getFileList(String basePath) {
        return getFileList(basePath, false);
    }

    @Override
    public AjaxResult getDirFileList(String basePath) {
        return getFileList(basePath, true);
    }

    @Override
    public void getFileIcon(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String filePath = urlPathToFilePath(request, "/file/getFileIcon");
        String fileAbPath = paramsPathToAbPathByUserId(filePath);
        if (fileAbPath == null) {
            WebUtils.renderString(response, AjaxResult.failure("传入filePath路径有错" + filePath));
            return;
        }
        WebUtils.getFileIcon(fileAbPath, request, response);
    }

    @Override
    public void getFileRange(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String filePath = urlPathToFilePath(request, "/file/getFileRange");
        String fileAbPath = paramsPathToAbPathByUserId(filePath);
        if (fileAbPath == null) {
            WebUtils.renderString(response, AjaxResult.failure("传入filePath路径有错" + filePath));
            return;
        }
        WebUtils.rangeReadFile(fileAbPath, request, response);
    }

    public static String urlPathToFilePath(HttpServletRequest request, String removeStart) {
        String requestURI = request.getRequestURI();
        String substring = requestURI.substring(requestURI.indexOf(removeStart) + removeStart.length());
        String filePath = Arrays.stream(substring.split("/")).map(i -> {
            try {
                return URLDecoder.decode(i, "UTF-8");
            } catch (UnsupportedEncodingException e) {
                throw new RuntimeException(e);
            }
        }).collect(Collectors.joining("/"));
        return filePath;
    }

    @Override
    public void getFile(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String filePath = urlPathToFilePath(request, "/file/getFile");
        String fileAbPath = paramsPathToAbPathByUserId(filePath);
        if (fileAbPath == null) {
            WebUtils.renderString(response, AjaxResult.failure("传入filePath路径有错" + filePath));
            return;
        }
        WebUtils.readFile(fileAbPath, request, response);
    }

    @Override
    public AjaxResult getTextFile(String filePath) throws IOException {
        String fileAbPath = paramsPathToAbPathByUserId(filePath);
        if (fileAbPath == null) {
            return AjaxResult.failure("传入filePath路径有错" + filePath);
        }
        String string = FileUtilsZX.readFileToString(new File(fileAbPath), StandardCharsets.UTF_8);
        return AjaxResult.success(string);
    }

    private String paramsPathToAbPathByUserId(String filePath, String userId) throws IOException {
        String baseFilePath1 = userId == null ? getBaseFilePathByUserId() : getBaseFilePathByUserId(userId);
        String fileAbPath = PathUtils.pathResolve(baseFilePath1, filePath);
        boolean childPath = PathUtils.isChildPath(fileAbPath, baseFilePath1);
        if (!childPath) {
            return null;
        }
        return fileAbPath;
    }

    private String paramsPathToAbPathByUserPath(String filePath, String path) throws IOException {
        String baseFilePath1 = path == null ? getBaseFilePathByUserId() : getBaseFilePathByUserPath(path);
        String fileAbPath = PathUtils.pathResolve(baseFilePath1, filePath);
        boolean childPath = PathUtils.isChildPath(fileAbPath, baseFilePath1);
        if (!childPath) {
            return null;
        }
        return fileAbPath;
    }

    private String paramsPathToAbPathByUserId(String filePath) throws IOException {
        return paramsPathToAbPathByUserId(filePath, null);
    }

    private List<String> paramsPathToAbPathByUserId(List<String> filePaths, String userId) throws IOException {
        List<String> newList = new LinkedList<>();
        for (String filePath : filePaths) {
            String s = paramsPathToAbPathByUserId(filePath, userId);
            if (s == null) {
                return null;
            }
            newList.add(s);
        }
        return newList;
    }

    private List<String> paramsPathToAbPathByUserPath(List<String> filePaths, String path) throws IOException {
        List<String> newList = new LinkedList<>();
        for (String filePath : filePaths) {
            String s = paramsPathToAbPathByUserPath(filePath, path);
            if (s == null) {
                return null;
            }
            newList.add(s);
        }
        return newList;
    }

    private List<String> paramsPathToAbPathByUserId(List<String> filePaths) throws IOException {
        return paramsPathToAbPathByUserId(filePaths, null);
    }

    @Override
    public AjaxResult saveTextFile(String filePath, String content) throws IOException {
        String fileAbPath = paramsPathToAbPathByUserId(filePath);
        if (fileAbPath == null) {
            return AjaxResult.failure("传入filePath路径有错" + filePath);
        }
        FileUtilsZX.write(new File(fileAbPath), content, StandardCharsets.UTF_8, false);
        return AjaxResult.success("保存成功");
    }

    @Override
    public AjaxResult deleteFileOrDir(List<String> filePaths) throws IOException {
        List<String> fileAbPath = paramsPathToAbPathByUserId(filePaths);
        if (fileAbPath == null || fileAbPath.contains(baseFilePath)) {
            return AjaxResult.failure("传入filePath路径有错" + filePaths);
        }
        for (String s : fileAbPath) {
            FileUtilsZX.forceDelete(new File(s));
        }
        return AjaxResult.success("删除完成");
    }

    @Override
    public AjaxResult copyFileOrDir(List<String> filePaths, String targetPath) throws IOException {
        List<String> fileAbPath = paramsPathToAbPathByUserId(filePaths);
        if (fileAbPath == null) {
            return AjaxResult.failure("传入filePaths路径有错" + filePaths);
        }
        String targetApPath = paramsPathToAbPathByUserId(targetPath);
        if (targetApPath == null) {
            return AjaxResult.failure("传入targetPath路径有错" + targetPath);
        }
        File file1 = new File(targetApPath);
        if (!file1.isDirectory()) {
            return AjaxResult.failure("传入targetPath路径不是一个文件夹" + targetPath);
        }
        for (String filePath : fileAbPath) {
            File file = new File(filePath);
            if (file.isDirectory()) {
                FileUtilsZX.copyDirectoryToDirectory(file, file1);
            } else {
                FileUtilsZX.copyFileToDirectory(file, file1);
            }
        }
        return AjaxResult.success("复制完成");
    }

    @Override
    public AjaxResult removeFileOrDir(List<String> filePaths, String targetPath) throws IOException {
        AjaxResult ajaxResult = copyFileOrDir(filePaths, targetPath);
        if (!ResultCode.SUCCESS.code().equals(ajaxResult.getCode())) {
            return ajaxResult;
        }
        List<String> fileAbPath = paramsPathToAbPathByUserId(filePaths);
        List<String> delAbPath = fileAbPath.stream()
                .filter(i -> !baseFilePath.equals(i))
                .collect(Collectors.toList());
        //删除旧文件
        System.gc();
        for (String s : delAbPath) {
            FileUtilsZX.forceDelete(new File(s));
        }
        return AjaxResult.success("移动完成");
    }

    @Override
    public AjaxResult uploaderFile(String basePath, MultipartFile file) throws IOException {
        String baseApPath = paramsPathToAbPathByUserId(basePath);
        if (baseApPath == null) {
            return AjaxResult.failure("传入basePath路径错误" + basePath);
        }
        File file1 = new File(baseApPath);
        if (file.isEmpty()) {
            return AjaxResult.failure("file文件为空" + file);
        }
        if (!file1.exists()) {
            FileUtilsZX.createFile(file1.getAbsolutePath());
        }
        // 将MultipartFile写入硬盘
        file.transferTo(file1);
        return AjaxResult.success("上传完成");
    }

    @Autowired
    private ShareFileMapper shareFileMapper;

    @Override
    public AjaxResult shareFileOrDir(List<ShareFile> shareFiles) {
        SysUserWithRole user = (SysUserWithRole) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        List<String> result = new LinkedList<>();
        for (ShareFile shareFile : shareFiles) {
            shareFile.setUserId(user.getUserId());
            LambdaQueryWrapper<ShareFile> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(ShareFile::getUserId, user.getUserId())
                    .eq(ShareFile::getFilePath, shareFile.getFilePath());
            ShareFile shareFile1 = shareFileMapper.selectOne(queryWrapper);
            if (shareFile1 == null) {
                shareFile.setShareFileId(WebUtils.uuid64());
                shareFileMapper.insert(shareFile);
                result.add(shareFile.getShareFileId());
            } else {
                shareFile1.setEntTime(shareFile.getEntTime());
                shareFileMapper.updateById(shareFile1);
                result.add(shareFile1.getShareFileId());
            }
        }
        return AjaxResult.success(result);
    }

    @Override
    public AjaxResult shareFileList(ShareFileSearch shareFile, int pageNum, int pageSize) {
        SysUserWithRole user = (SysUserWithRole) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        Page<ShareFile> shareFilePage = new Page<>(pageNum, pageSize);
        LambdaQueryWrapper<ShareFile> queryWrapper = new LambdaQueryWrapper<>();
        if (!user.isInitAdmin() || !user.isAdmin()) {
            queryWrapper.eq(ShareFile::getUserId, user.getUserId());
        }
        if (StringUtilsZX.isNotEmpty(shareFile.getFilePath())) {
            queryWrapper.like(ShareFile::getFilePath, shareFile.getFilePath());
        }
        if (shareFile.getEntTimeEnd() != null) {
            queryWrapper.le(ShareFile::getEntTime, shareFile.getEntTimeEnd());
        }
        if (shareFile.getEntTimeStart() != null) {
            queryWrapper.ge(ShareFile::getEntTime, shareFile.getEntTimeStart());
        }
        Page<ShareFile> shareFilePage1 = shareFileMapper.selectPage(shareFilePage, queryWrapper);
        return AjaxResult.success(shareFilePage1);
    }

    @Override
    public AjaxResult shareFileDelete(List<String> shareFileDelete) {
        if (CollectionUtils.isEmpty(shareFileDelete)) {
            return AjaxResult.success("参数错误");
        }
        shareFileMapper.deleteBatchIds(shareFileDelete);
        return AjaxResult.success("删除完成");
    }

    @Override
    public AjaxResult shareFileUpdate(ShareFile shareFile) {
        shareFileMapper.updateById(shareFile);
        return AjaxResult.success("更新完成");
    }

    @Override
    public AjaxResult hasFile(String basePath) throws IOException {
        String baseApPath = paramsPathToAbPathByUserId(basePath);
        if (baseApPath == null) {
            return AjaxResult.failure("传入basePath路径错误" + basePath);
        }
        File file1 = new File(baseApPath);
        if (file1.exists()) {
            return AjaxResult.success("1"); // 1 存在
        }
        return AjaxResult.success("0"); // 0 不存在

    }

    @Override
    public AjaxResult getFileInfo(String basePath) throws IOException {
        String baseApPath = paramsPathToAbPathByUserId(basePath);
        if (baseApPath == null) {
            return AjaxResult.failure("传入basePath路径错误" + basePath);
        }
        File file1 = new File(baseApPath);
        if (file1.exists()) {
            FileResult fileResult = new FileResult();
            if (PathUtils.isEquals(baseFilePath, baseApPath)) {
                fileResult.setFileName("根目录");
            } else {
                fileResult.setFileName(file1.getName());
            }
            fileResult.setDirectory(file1.isDirectory());
            BasicFileAttributes basicFileAttributes = Files.readAttributes(file1.toPath(), BasicFileAttributes.class);
            FileTime creationTime = basicFileAttributes.creationTime();
            FileTime lastModifiedTime = basicFileAttributes.lastModifiedTime();
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            fileResult.setCreateTime(formatter.format(
                    LocalDateTime.ofInstant(creationTime.toInstant(), ZoneId.systemDefault())));
            fileResult.setLastUpdateTime(formatter.format(
                    LocalDateTime.ofInstant(lastModifiedTime.toInstant(), ZoneId.systemDefault())));
            fileResult.setPath(basePath);
            if (file1.isDirectory()) {
                FileFilter fileFilter = new FileFilter();
                DirFilter dirFilter = new DirFilter();
                FileUtilsZX.listFilesAndDirs(file1, fileFilter, dirFilter);
                fileResult.setFileCount(fileFilter.getFileCount());
                fileResult.setDirCount(dirFilter.getFileCount());
                fileResult.setSize(FileUtilsZX.sizeOfDirectory(file1));
            } else {
                fileResult.setSize(FileUtilsZX.sizeOf(file1));
            }
            return AjaxResult.success(fileResult);
        } else {
            return AjaxResult.failure("文件已不存在");
        }
    }

    @Override
    public AjaxResult createFile(String basePath) throws IOException {
        String baseApPath = paramsPathToAbPathByUserId(basePath);
        if (baseApPath == null) {
            return AjaxResult.failure("传入basePath路径错误" + basePath);
        }
        FileUtilsZX.createFile(baseApPath);
        return AjaxResult.success("文件创建完成");
    }

    @Override
    public AjaxResult createDir(String basePath) throws IOException {
        String baseApPath = paramsPathToAbPathByUserId(basePath);
        if (baseApPath == null) {
            return AjaxResult.failure("传入basePath路径错误" + basePath);
        }
        boolean mkdirs = new File(baseApPath).mkdirs();
        if (mkdirs) {
            return AjaxResult.success("文件夹创建完成");
        } else {
            return AjaxResult.failure("文件夹创建失败");
        }
    }

    @Autowired
    private DownloadFileMapper downloadFileMapper;

    @Override
    public AjaxResult downloadFileOrDir(String basePath, List<String> filePaths) throws IOException {
        List<String> fileApPaths = paramsPathToAbPathByUserId(filePaths);
        if (CollectionUtils.isEmpty(fileApPaths)) {
            return AjaxResult.failure("传入filePaths路径错误" + filePaths);
        }
        DownloadFile downloadFile = new DownloadFile();
        String join = StringUtilsZX.join(filePaths, WebUtils.split);
        // 偏移两分钟，避免出现下载失败的情况
        downloadFile.setEntTime(DateUtil.offsetMinute(new Date(), 2));
        downloadFile.setFilePaths(join);
        downloadFile.setDownloadFileId(WebUtils.uuid64());
        SysUserWithRole user = (SysUserWithRole) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        downloadFile.setUserId(user.getUserId());
        downloadFile.setBasePath(basePath);
        if (fileApPaths.size() == 1) {
            String filepath = fileApPaths.get(0);
            File file = new File(filepath);
            if (file.isDirectory()) {
                downloadFile.setDownloadName(file.getName() + ".zip");
            } else {
                downloadFile.setDownloadName(file.getName());
            }
        } else {
            downloadFile.setDownloadName("下载.zip");
        }
        downloadFileMapper.insert(downloadFile);
        return AjaxResult.success(downloadFile);
    }

    @Override
    public void downloadFileOrDirById(String downLoadId, HttpServletRequest request, HttpServletResponse response) throws IOException {
        DownloadFile downloadFile = downloadFileMapper.selectById(downLoadId);
        boolean before = downloadFile.getEntTime().before(new Date());
        if (before) {
            WebUtils.renderString(response, AjaxResult.failure("下载失败"));
            return;
        }
        String[] split = downloadFile.getFilePaths().split(WebUtils.split);
        List<String> list = Arrays.asList(split);
        String userpath = "";
        if (!UserDetailsServiceImpl.INIT_ADMIN_ID.equals(downloadFile.getUserId())) {
            userpath = sysUserMapper.selectById(downloadFile.getUserId()).getUserpath();
        }
        List<String> fileApPaths = paramsPathToAbPathByUserPath(list, userpath);
        if (fileApPaths == null) {
            WebUtils.renderString(response, AjaxResult.failure("下载失败"));
            return;
        }
        String zipFileBasePath = paramsPathToAbPathByUserPath(downloadFile.getBasePath(), userpath);
        if (fileApPaths.size() == 1) {
            String filepath = fileApPaths.get(0);
            File file = new File(filepath);
            if (file.isDirectory()) {
                WebUtils.downloadZipFile(fileApPaths, zipFileBasePath, file.getName() + ".zip", response);
            } else {
                WebUtils.downloadFile(file, response);
            }
        } else {
            WebUtils.downloadZipFile(fileApPaths, zipFileBasePath, "下载.zip", response);
        }
    }

    @Override
    public AjaxResult getDiskInfo() {
        File win = new File(baseFilePath);
        if (win.exists()) {
            long total = win.getTotalSpace();
            long freeSpace = win.getFreeSpace();
            long use = total - freeSpace;
            Map<String, Long> result = new HashMap<>();
            result.put("total", total);
            result.put("freeSpace", freeSpace);
            result.put("use", use);
            return AjaxResult.success(result);
        }
        return AjaxResult.failure("获取失败");
    }

    @Override
    public AjaxResult getShareFile(String id) {
        ShareFileWithUser shareFile = shareFileMapper.selectWithUserById(id);
        if (UserDetailsServiceImpl.INIT_ADMIN_ID.equals(shareFile.getUserId())) {
            shareFile.setUsername(UserDetailsServiceImpl.adminUserName);
            shareFile.setUserpath("/");
        }
        return AjaxResult.success(shareFile);
    }

    @Override
    public AjaxResult shareFileDown(ShareFileWithUser shareFileWithUser) throws IOException {
        String fileApPath = paramsPathToAbPathByUserPath(shareFileWithUser.getFilePath(), shareFileWithUser.getUserpath());
        if (fileApPath == null) {
            return AjaxResult.failure("下载失败");
        }
        DownloadFile downloadFile = new DownloadFile();
        downloadFile.setFilePaths(shareFileWithUser.getFilePath());
        downloadFile.setUserId(shareFileWithUser.getUserId());
        // 偏移两分钟，避免出现下载失败的情况
        downloadFile.setEntTime(DateUtil.offsetMinute(new Date(), 2));
        downloadFile.setDownloadFileId(WebUtils.uuid64());
        File file = new File(fileApPath);
        downloadFile.setDownloadName(file.getName());
        downloadFileMapper.insert(downloadFile);
        return AjaxResult.success(downloadFile);
    }

    @Override
    public AjaxResult rename(String filePath, String newName) throws IOException {
        String fileApPath = paramsPathToAbPathByUserId(filePath);
        if (fileApPath == null) {
            return AjaxResult.failure("传入filePath路径错误" + filePath);
        }
        File file = new File(fileApPath);
        FileUtil.rename(file, newName, true);
        return AjaxResult.success("重命名成功");
    }
}
